#include "common.hpp"

static const char *optString = "p:b:c:l:j:s:h?";

static const struct option longOpts[] = {
	{ "help", no_argument, NULL, 'h' },
	{ "pack", required_argument, NULL, 'p'},
	{ "bottles", required_argument, NULL, 'b'},
	{ "caps", required_argument, NULL, 'c' },
	{ "labels", required_argument, NULL, 'l'},
	{ "juice", required_argument, NULL, 'j'},
	{ "sugar", required_argument, NULL, 's'},
	{ 0,0,0,0 }
};

int main (int argc, char* const argv[]) {

	ipc::semaphore_with_exceptions mutex_repositor ( my_ftok(MUTEX|REPOSITOR),0 );

	ipc::messages_queue_with_exceptions repositor_salida ( my_ftok(MSG_QUEUE|REPOSITOR) );

	int longIndex = 0;
	int option = 0;
	bool seguir = true;
	bool ayuda = false;
	long pack = 1; // cantidad de elementos de cada recurso enviados por solicitud.
	long botellas = 0;
	long tapas = 0;
	long etiquetas = 0;
	long jugos = 0;
	long azucar = 0;
	size_t reposiciones = 0;

	// Interpretación de parametros.
	while ( (option = getopt_long(argc,argv,optString,longOpts,&longIndex) ) != EOF ) {
		switch (option) {
			case '?':
			case 'h':
			case ':':
				ayuda = true;
				break;
			case 'p':
				pack = parse(optarg,0);	
				break;
			case 'b':
				botellas = parse(optarg,0);
				break;
			case 'c':
				tapas = parse(optarg,0);
				break;
			case 'l':
				etiquetas = parse(optarg,0);
				break;
			case 'j':
				jugos = parse(optarg,0);
				break;
			case 's':
				azucar = parse(optarg,0);
				break;
			default:
				break;
		}
	}

	if ( ayuda || (pack <= 0) || botellas<0 || tapas<0 || etiquetas<0 || jugos<0 ) {
		std::cerr << "Proceso de control de envio de botellas a las llenadoras." << std::endl;
		std::cerr << "\tUso: " << argv[0] << " [-h]" << std::endl;
		std::cerr << "\tParametros:" << std::endl;
		std::cerr << "\t\t-h,--help:\tMostrar este mensaje de ayuda." << std::endl;
		std::cerr << "\t\t-p,--pack:\tCantidad de elementos a enviar como respuesta a cada petición de recursos." << std::endl;
		std::cerr << "\t\t-b,--bottles:\tCantidad de botellas vacias disponibles para reposición." << std::endl;
		std::cerr << "\t\t-c,--caps:\tCantidad de tapas disponibles para reposición." << std::endl;
		std::cerr << "\t\t-l,--labels:\tCantidad de etiquetas disponibles para reposición." << std::endl;
		std::cerr << "\t\t-s,--sugar:\tCantidad de dosis de azucar." << std::endl;

		return (ayuda) ? 0 : -1;
	}

	sPedido pedido;
	pedido.tipo = 0;
	pedido.recurso = NINGUNO;

	sReposicion reposicion;
	reposicion.tipo = 0;
	reposicion.recurso = NINGUNO;
	reposicion.cantidad = pack;

	std::map<pid_t, ipc::semaphore> semaforos;

	std::map<int, size_t> almacen;
	almacen[BOTELLA] = botellas;
	almacen[TAPA] = tapas;
	almacen[ETIQUETA] = etiquetas;
	almacen[JUGO] = jugos;
	almacen[AZUCAR] = azucar;

	const std::string color = parse(25+REPOSITOR) + "m";
	const std::string normal = "\033[0;"+color;
	const std::string bold = "\033[1;"+color;
	logger.setColor(normal);
	try {
		mutex_repositor.get(PERMITS);
		repositor_salida.get(PERMITS);

		logger << "Repositor: Inicio." << nl;
		logger << "Repositor: Recursos disponibles: { ";
		for (std::map<int, size_t>::const_iterator it = almacen.begin(); it != almacen.end(); ++it) {
			logger << eRecursoToStr(it->first);

			if (it->first != AZUCAR)
				logger << "s: ";
			else
				logger << ": ";
			logger << it->second << " ";
		}
		logger << "}." <<nl;

		while (seguir) {
			mutex_repositor.wait();
			pedido.tipo = 0;
			logger << "Repositor: Esperando petición." << nl;
			repositor_salida.receive(pedido,PEDIR_RECURSO);

			if ( (pedido.tipo == PEDIR_RECURSO) && (almacen.count(pedido.recurso) == 1) ) {
				reposicion.tipo = pedido.origen;
				reposicion.recurso = pedido.recurso;
			
				// Agrego el semaforo al registro si no existia.
				if ((pedido.sem_key != 0) && (semaforos.count(pedido.origen) == 0) ) {
					logger << "Repositor: Asignando el semaforo con clave: " << std::hex << pedido.sem_key << std::dec << "." << nl;
					ipc::semaphore_with_exceptions sem(pedido.sem_key,pedido.sem_num);
					semaforos[pedido.origen] = ipc::semaphore_with_exceptions(pedido.sem_key,pedido.sem_num);
					semaforos.at(pedido.origen).get(PERMITS);
				}

				// Obtengo un pack del almacen.
				if (almacen[pedido.recurso] > (size_t)pack) {
					reposicion.cantidad = pack;	
					almacen[pedido.recurso]-= pack;
				} else {
					reposicion.cantidad = almacen[pedido.recurso];
					almacen[pedido.recurso] = 0;
				}

				if (reposicion.cantidad > 0) {
					logger << "Repositor: Enviando " << eRecursoToStr(pedido.recurso) << " x" << reposicion.cantidad << " a la estación " << pedido.origen <<  ". Remanente: "  << almacen[pedido.recurso] << "." << nl;
					repositor_salida.send(reposicion);
					reposiciones++;

					if (pedido.sem_key != 0) {
						semaforos.at(pedido.origen).signal();
					}
				} else {
					logger.setColor(bold);
					logger << "Repositor: Recurso agotado, " << eRecursoToStr(pedido.recurso) << "." << nl;
					logger.setColor(normal);
					logger << "Repositor: Recursos disponibles: { ";
					for (std::map<int, size_t>::const_iterator it = almacen.begin(); it != almacen.end(); ++it)
						logger << eRecursoToStr(it->first) << "s: " << it->second << " ";
					logger << "}." <<nl;
				}
			} else {
				error << "Repositor: Ignorando pedido incorrecto de" << pedido.origen << "." << nl;
				error << "pedido: " << pedido.tipo << "(" << PEDIR_RECURSO << ")," << pedido.origen << "," << pedido.recurso << ":" << eRecursoToStr(pedido.recurso) << nl;
				error << "¿El recurso existe en el almacen?: " << std::boolalpha << (almacen.count(pedido.recurso) == 1) << nl;

			}
			mutex_repositor.signal();
		}
	} catch (ipc::exception& e) {
		logger << "Repositor: " << e.what() << "." << nl;
		exit(-1);
	}
	logger << "Repositor: Fin." << nl;

	return 0;
}
